package com.luckyframe.project.jmeter.consumer.watch;

import cn.hutool.extra.mail.MailUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.luckyframe.common.utils.StringUtils;
import com.luckyframe.common.utils.spring.SpringUtils;
import com.luckyframe.project.jmeter.consumer.SampleHandler;
import com.luckyframe.project.jmeter.jmeterAlertConfig.domain.AlertCheckEntity;
import com.luckyframe.project.jmeter.jmeterAlertConfig.domain.AlertChecker;
import com.luckyframe.project.jmeter.jmeterAlertConfig.domain.CheckResult;
import com.luckyframe.project.jmeter.jmeterAlertConfig.domain.JmeterAlertConfig;
import com.luckyframe.project.jmeter.jmeterAlertRecord.domain.JmeterAlertRecord;
import com.luckyframe.project.jmeter.jmeterAlertRecord.service.IJmeterAlertRecordService;
import com.luckyframe.project.jmeter.jmeterTestCase.domain.JmeterTestCase;
import com.luckyframe.project.jmeter.jmeterTestPlan.controller.JmeterTestPlanController;
import com.luckyframe.project.jmeter.jmeterTestPlan.domain.JmeterTestPlan;
import com.luckyframe.project.jmeter.jmeterTestPlanRecord.domain.JmeterTestPlanRecord;
import com.luckyframe.project.jmeter.jmeterTestPlanRecord.enums.JmeterPlanRecordStatus;
import com.luckyframe.project.jmeter.jmeterTestPlanRecord.service.IJmeterTestPlanRecordService;
import com.luckyframe.project.jmeter.jmeterUser.domain.JmeterUser;
import com.luckyframe.project.jmeter.jmeterUser.service.IJmeterUserService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.jmeter.report.core.DataContext;
import org.apache.jmeter.report.core.Sample;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public class CaseWatchTask implements Runnable {

    private static final Logger logger = LoggerFactory.getLogger(JmeterTestPlanController.class);

    private SampleHandler sampleHandler = new SampleHandler();

    private JmeterTestCase jmeterTestCase;

    private JmeterTestPlan jmeterTestPlan;

    private BlockingQueue<Sample> samples;

    private JmeterAlertConfig alertConfig;

    private JmeterTestPlanRecord planRecord;

    private IJmeterAlertRecordService jmeterAlertRecordService = SpringUtils.getBean(IJmeterAlertRecordService.class);

    private IJmeterUserService jmeterUserService = SpringUtils.getBean(IJmeterUserService.class);

    private IJmeterTestPlanRecordService planRecordService = SpringUtils.getBean(IJmeterTestPlanRecordService.class);


    private long lastSendTime = -99999L;

    private volatile boolean running = true;

    private List<Sample> samplesTobeConsumed = new ArrayList<>();

    private List<Sample> totalSamples = new ArrayList<>();

    private int prevSize=0;

    private boolean hasSendAlert=false;


    public CaseWatchTask(JmeterTestPlan jmeterTestPlan, JmeterTestCase jmeterTestCase,
                         BlockingQueue<Sample> samples, JmeterAlertConfig alertConfig,JmeterTestPlanRecord planRecord) {
        this.jmeterTestPlan = jmeterTestPlan;
        this.jmeterTestCase = jmeterTestCase;
        this.samples = samples;
        this.alertConfig = alertConfig;
        this.planRecord=planRecord;
    }

    public CaseWatchTask(JmeterTestPlan plan, JmeterTestCase testCase, BlockingQueue<Sample> samples) {
        this.jmeterTestPlan = plan;
        this.jmeterTestCase = testCase;
        this.samples = samples;

    }

    @Override
    public void run() {
        try {
            while (running) {
                if (consumeSamples()) continue;
            }

        } catch (Exception e) {
            logger.error("CaseWatchTask error occured plan={} case={}",jmeterTestPlan.getName(),jmeterTestCase.getCaseName(), e);
        }
    }

    private boolean consumeSamples() throws InterruptedException {

        Sample sample = samples.poll(1, TimeUnit.MILLISECONDS);

        if (sample != null) {
            samplesTobeConsumed.add(sample);
            totalSamples.add(sample);

            planRecord.setStartTime(new Date(totalSamples.get(0).getStartTime()));
            //update every 2s
            if (System.currentTimeMillis() - lastSendTime >2000){
                planRecordService.updateJmeterTestPlanRecord(planRecord);
            }

            int allThreads = sample.getAllThreads();

            if (alertConfig!=null && allThreads < alertConfig.getThreadThreshold()) {
                return true;
            }

        }

        //如果没有发送过 则立即发送
        if(!hasSendAlert && totalSamples.size()>0){

            DataContext dataContext = sampleHandler.generateCaseStatistics(totalSamples);
            checAndSendAlert(dataContext);

        }else {

            //send every 5s
            if (System.currentTimeMillis() - lastSendTime > 5000) {

                if (totalSamples.size() > 0 && totalSamples.size()>prevSize) {

                    DataContext dataContext = sampleHandler.generateCaseStatistics(totalSamples);

                    checAndSendAlert(dataContext);

                    samplesTobeConsumed.clear();
                    lastSendTime = System.currentTimeMillis();
                    prevSize=totalSamples.size();

                }
            }
        }

        return false;
    }


    private void checAndSendAlert(DataContext dataContext) {

        if(alertConfig==null){
            return;
        }

        String statisticsString = Objects.toString(dataContext.get("overallStatistics"), "{}");

        JSONObject jsonObject = JSON.parseObject(statisticsString);

        JSONArray items = jsonObject.getJSONArray("items");

        if (items != null && items.size() > 0) {

            for (int i = 0; i < items.size(); i++) {

                JSONArray data = items.getJSONObject(i).getJSONArray("data");

                String sampleName = data.getString(0);
                Double tps = data.getDouble(10);
                Double avgResp = data.getDouble(4);
                Double errorRatio = data.getDouble(3);
                Double line95 = data.getDouble(8);

                AlertCheckEntity alertCheckEntity = new AlertCheckEntity();
                alertCheckEntity.setTps(tps);
                alertCheckEntity.setSuccessRatio(100 - errorRatio);
                alertCheckEntity.setAvgRespTime(avgResp);
                alertCheckEntity.setLine95(line95);
                alertCheckEntity.setSampleName(sampleName);

                List<CheckResult> checkResults = AlertChecker.checkAll(alertConfig, alertCheckEntity);

                if (CollectionUtils.isNotEmpty(checkResults)) {
                    for (CheckResult checkResult : checkResults) {

                        if (StringUtils.isEmpty(checkResult.getMsg())) {
                            continue;
                        }

                        saveAndMailAlertRecord(checkResult);
                    }
                }
            }
        }
    }


    private void saveAndMailAlertRecord(CheckResult checkResult) {

        JmeterAlertRecord jmeterAlertRecord = new JmeterAlertRecord();
        jmeterAlertRecord.setModuleId(jmeterTestPlan.getRelModuleId());
        jmeterAlertRecord.setModuleName(jmeterTestPlan.getRelModuleName());
        jmeterAlertRecord.setProjectId(jmeterTestPlan.getRelProjectId().longValue());
        jmeterAlertRecord.setProjectName(jmeterTestPlan.getRelProjectName());
        jmeterAlertRecord.setInterfaceName(jmeterTestPlan.getInterfaceName());
        jmeterAlertRecord.setCaseId(jmeterTestCase.getId());
        jmeterAlertRecord.setCaseName(jmeterTestCase.getCaseName());
        jmeterAlertRecord.setType(checkResult.getAlertType().getKey());
        jmeterAlertRecord.setMsg(checkResult.getMsg());
        jmeterAlertRecord.setCreateTime(new Date());

        jmeterAlertRecordService.insertJmeterAlertRecord(jmeterAlertRecord);

        List<JmeterUser> jmeterUsers = jmeterUserService.findEnabled();
        if (CollectionUtils.isNotEmpty(jmeterUsers)) {
            for (JmeterUser jmeterUser : jmeterUsers) {
                String email = jmeterUser.getEmail();

                if (org.apache.commons.lang3.StringUtils.isEmpty(email)) {
                    continue;
                }

                String title = "【压测监控告警】测试计划【" + jmeterTestPlan.getName() + "】测试用例【" + jmeterTestCase.getCaseName() + "】";

                MailUtil.send(email, title, jmeterAlertRecord.getMsg(), false);
            }
        }
    }


    public void start(){
        running = true;
    }

    public void stop() {

        try {

            running = false;

            consumeSamples();

            if(planRecord!=null && totalSamples.size()>0){

                planRecord.setEndTime(new Date(totalSamples.get(totalSamples.size()-1).getEndTime()));
                planRecord.setStatus(JmeterPlanRecordStatus.SUCCESS.getCode());
                planRecordService.updateJmeterTestPlanRecord(planRecord);
            }

        }catch (Exception ex){
            logger.error("CaseWatchTask stop error  plan={} case={}",jmeterTestPlan.getName(),jmeterTestCase.getCaseName(),ex);
        }
    }
}
